#include <curl/curl.h>
#include <sstream>
#include <iostream>
#include "HttpClient.h"
#include <assert.h>

CHttpClient::~CHttpClient()
{
}

int64_t CHttpClient::GetFileSize(const std::string &url)
{
    auto curl = curl_easy_init();
    if (!curl)
    {
        curl_easy_cleanup(curl);
        return -1;
    }
    double filesize = -1; //文件大小

    curl_easy_setopt(curl,CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_HEADER, 1); 
    curl_easy_setopt(curl, CURLOPT_NOBODY, 1); 
    CURLcode res_code = curl_easy_perform(curl); //请求
    if (res_code != CURLE_OK)
    {
        curl_easy_cleanup(curl);
        return -1;
    }
    
    curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &filesize); 
    curl_easy_cleanup(curl);

    std::cout << "获取文件大小完成:" << (int64_t)filesize  << std::endl;
    return filesize;
}

size_t CHttpClient::WriteBlock(void *data, size_t size, size_t nmemb, void *clientp)
{
    CHttpClient *ptr = (CHttpClient*)clientp;
    assert(ptr);
    size_t realsize = size * nmemb;
    ptr->m_blockData->WriteData(data, realsize);
    return realsize;
}

bool CHttpClient::Download(const std::string &url, int64_t offset, int64_t blocksize)
{
    auto curl = curl_easy_init();
    if (!curl)
    {
        curl_easy_cleanup(curl);
        return false;
    }
    auto rangeEnd = offset + blocksize - 1;
    std::cout << "开始下载数据，区间:" << offset << "-" <<  rangeEnd  << ",块大小:" << blocksize << std::endl;
	m_blockData = std::make_shared<BlockData>(offset, blocksize);
	curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_FTP_RESPONSE_TIMEOUT, 10);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CHttpClient::WriteBlock);
    std::stringstream ss;
    ss << offset << "-" << rangeEnd;
	curl_easy_setopt(curl, CURLOPT_RANGE, ss.str().c_str());
	CURLcode res_code = curl_easy_perform(curl);
    if (res_code != CURLE_OK)
    {
        curl_easy_cleanup(curl);
        return false;
    }
	std::cout << "下载数据:" << offset << "-" << rangeEnd << ", 获取数据：" << m_blockData->readSize << "完成" << std::endl;
	curl_easy_cleanup(curl);
    return true;
}

std::iostream &CHttpClient::GetStream()
{
    if (!m_blockData || m_blockData->readSize != m_blockData->total)
    {
        throw std::runtime_error("下载数据与实际数据不符");
    }
    
    return m_blockData->ss;
}


size_t CHttpClient::ReadHeader(void *data, size_t size, size_t nmemb, void *param)
{
    std::string *contextMd5 = (std::string*)param;
    assert(contextMd5);
    std::string strHeader((char*)data, size*nmemb);
    size_t pos;
    if ((pos = strHeader.find("content-md5:")) != std::string::npos)
    {
        strHeader = strHeader.substr(sizeof("content-md5:"));
        auto enter = strHeader.find_last_not_of("\r\n");
        if (enter != std::string::npos)
        {
            *contextMd5 = strHeader.erase(enter + 1);
        }
        else
        {
            *contextMd5 = strHeader.erase(0);
        }
    } 
    return size*nmemb;  
}

std::string CHttpClient::GetContextMd5(const std::string &url)
{
    auto curl = curl_easy_init();
    if (!curl)
    {
        curl_easy_cleanup(curl);
        return std::string();
    }


    curl_easy_setopt(curl,CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_HEADER, 1); 
    curl_easy_setopt(curl, CURLOPT_NOBODY, 1); 

    std::string strMd5;
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, &strMd5);
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, CHttpClient::ReadHeader);
    CURLcode res_code = curl_easy_perform(curl); //请求
    if (res_code != CURLE_OK)
    {
        curl_easy_cleanup(curl);
        return std::string();
    }
    
    curl_easy_cleanup(curl);
    std::cout << "获取Md5完成:" << strMd5  << std::endl;
    return strMd5;
}